import json
from dataclasses import dataclass
from typing import List

import pandas as pd

from PySide2.QtWidgets import QTableWidget, QAbstractItemView, QTableWidgetItem, QHeaderView
from pandas import DataFrame

from common.pd import get_tb_data, change_dict_to_db
from common.pyside2_table.col import ColItem, add_to_table
from common.pyside2_table.qt_wgt import set_item_color
from common.tb_model import db_to_models


@dataclass
class BaseModel:
    id: int = 0
    status: int = 0


class Base_Table(QTableWidget):
    """功能：
        1、展示数据到QTableWidget
        2、记录修改的数据
        3、保存修改数据至数据库
    """

    def __init__(self, tb_name, col_model_list=None, data_type="df", key_col_name="id", find_condition=None):
        super().__init__()
        self.data_type = data_type  # 数据的类型，"df" 或者是Model类
        self.tb_name = tb_name
        self.find_condition = find_condition
        self.key_col_name = key_col_name  # 用于查询唯一数据的关列名
        self.datas_key_col_value_list = []
        self.json_attr_name = ""  # 确定行数的属性名（即保存json数据的列名）
        self.json_dic_key_to_col_name = ""  # 行的关键字段 ,json 中字典的key的列名
        self.col_model_list = col_model_list  # 数据列的控件对象列表
        self.col_name_list = []  # 列名列表
        self.col_name_dic = dict()  # 列名字典
        self.row_base_cols_name: list = []  # 只占一行的列名
        self.col_header_names = []  # 表头名
        self.datas: DataFrame = []  # 数据
        self.datas_show = []  # 要展示的对象列表
        self.is_original = True  # 是否是初始状态
        self.setEditTriggers(QAbstractItemView.DoubleClicked)
        self.set_col_header(self.col_model_list)
        self.itemChanged.connect(self.item_changed)
        self.change_dic = dict()
        self.key_col_index = self.col_name_list.index(self.key_col_name)
        self.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)

    def __get_col_model_dic__(self, col_model_list: List[ColItem]) -> None:
        """
        根据列模型信息设置列的标签名、列模型字典等信息
        """
        self.col_model_list = col_model_list
        if col_model_list:
            self.col_name_list = []
            self.col_name_dic = dict()
            self.col_header_names = []
            for com_model in col_model_list:
                self.col_name_list.append(com_model.col_name)
                self.col_name_dic[com_model.col_name] = com_model
                self.col_header_names.append(com_model.col_nickname)

    def set_col_header(self, col_model_list=None):
        """
        设置列名，列是否隐藏、列宽等信息
        """
        if not col_model_list:
            col_model_list = self.col_model_list
        self.__get_col_model_dic__(col_model_list)
        if self.col_header_names:
            self.setColumnCount(len(self.col_header_names))
            self.setHorizontalHeaderLabels(self.col_header_names)
            col_name_list = self.col_name_list
            for index, col_name in enumerate(col_name_list):
                col_model: ColItem = self.col_name_dic[col_name]
                self.setColumnHidden(index, col_model.is_hide)
                if not col_model.is_hide and col_model.col_width:
                    self.setColumnWidth(index, col_model.col_width)

    def init_datas(self, datas):
        """根据获取的数据，并重置表格数据"""
        self.datas = datas
        self.datas_show = datas
        if type(datas) is list:
            self.datas_key_col_value_list = [getattr(model, self.key_col_name) for model in self.datas]
        if type(datas) is DataFrame:
            self.datas_key_col_value_list = datas[self.key_col_name].tolist()
        self.__insert_rows()

    # region model和df显示数据的方法及无数据的显示信息
    def __show_model_rows(self):
        self.is_original = True
        model_list: List[BaseModel] = self.datas_show
        if not model_list:
            self.no_data_show()
            return
        """处理思路"""
        self.setRowCount(0)
        for model in model_list:
            r_row_count = self.rowCount()  # 获取当前行数
            json_dic: dict = getattr(model, self.json_attr_name)  # 获取json字典
            json_keys = []
            # 确定一个drcs的行数，如何没有doc字典，插入一行
            if type(json_dic) is dict:
                json_keys = list(json_dic.keys())  # 获取文件编码列表
                insert_row_count = len(json_keys)  # 获取插入的行数
            else:
                insert_row_count = 1
            # 根据json 数据的信息设置插入的行数
            for i in range(insert_row_count):
                self.insertRow(r_row_count)
            # 给插入的每一行赋值
            for col_num, col in enumerate(self.col_name_list):
                col_model: ColItem = self.col_name_dic[col]
                # 获取列对应的对象属性
                if not col_model.value_attr_name:
                    col_model.value_attr_name = col_model.col_name
                model_attr_name = col_model.value_attr_name
                # 添加需合并的行
                if model_attr_name not in self.row_base_cols_name:
                    # 合同表格行
                    if insert_row_count > 1:
                        self.setSpan(r_row_count, col_num, insert_row_count, 1)
                    cell = (r_row_count, col_num)  # 形成行数和列数
                    add_to_table(self, col_model, model, cell)

                elif json_keys:
                    for doc_index in range(insert_row_count):
                        cell = (doc_index + r_row_count, col_num)
                        if model_attr_name == self.json_dic_key_to_col_name:
                            value = json_keys[doc_index]
                            add_to_table(self, col_model, value, cell)
                        else:
                            add_to_table(self, col_model, json_dic[json_keys[doc_index]], cell)
        self.is_original = False

    def __show_df_rows(self):
        self.is_original = True
        self.setRowCount(0)
        df_show_data: DataFrame = self.datas_show
        row_count, col_count = df_show_data.shape
        if not row_count:
            self.no_data_show()
        self.setRowCount(row_count)
        for col_num, col in enumerate(self.col_name_list):
            max_length = 0

            col_model: ColItem = self.col_name_dic[col]

            if not col_model.value_attr_name:
                col_model.value_attr_name = col_model.col_name
            df_col_name = col_model.value_attr_name

            if "date" in df_col_name:
                df_show_data[col] = pd.to_datetime(df_show_data[df_col_name]).dt.date
            col_ser = df_show_data[df_col_name]

            for index, value in enumerate(col_ser.values):
                cell = (index, col_num)  # 形成行数和列数
                add_to_table(self, col_model, value, cell)
            # 设置表格宽度值
            if col_model.col_width:
                self.setColumnWidth(col_num, col_model.col_width * 17)
            else:
                if not col_model.is_hide:
                    self.resizeColumnToContents(col_num)

        self.is_original = False

    def __insert_rows(self):
        self.is_original = True
        if self.data_type == "df":
            self.__show_df_rows()
        else:
            self.__show_model_rows()
        self.is_original = False

    def no_data_show(self):
        """无数据展示"""
        self.setRowCount(1)
        self.setSpan(0, 2, len(self.col_header_names) - 3, 1)
        self.setItem(0, 2, QTableWidgetItem("未查询到相关数据"))

    # 显示数据至表格

    # endregion

    # 重置数据
    def reset_data(self, datas=None):
        if self.data_type == "df":
            if type(datas) is not DataFrame:
                datas = get_tb_data(self.tb_name, self.find_condition)
        else:
            if not datas:
                datas = db_to_models(self.tb_name, self.data_type, self.find_condition)
        self.init_datas(datas)

    def show_data(self, show_data_df: DataFrame):
        self.datas_show = show_data_df
        self.__insert_rows()

    # region 记录单元格的值变化
    def item_changed(self, item: QTableWidgetItem, color="blue"):
        """单元格数据变化的处理
            处理思路： 获取单元格的行和列，即获取行的key_col_name的值，以及单元格的列名
            数据的key_col_name的值作为字典的key, k中包含一个字典 col_name :value
        """
        if not self.is_original:
            set_item_color(item, color)
            value = item.text()
            row_mun = item.row()
            col_num = item.column()
            col_name = self.col_name_list[col_num]
            self.add_value_to_change_dic(value, row_mun, col_name)

    def add_value_to_change_dic(self, value, row_index, col_name):
        if not self.is_original:

            key_value = self.item(row_index, self.key_col_index).text()
            if self.key_col_name == "id":
                key_value = int(key_value)

            # 如果存在json 字符串
            if self.json_dic_key_to_col_name:  # 存在json 字符串
                if col_name in self.row_base_cols_name:  # 列名在json字符串的字典中
                    # 获取json字典中key的列名
                    json_dict_key_index = self.col_name_list.index(self.json_dic_key_to_col_name)
                    # 获取json字典中key的的值
                    json_dict_key_value = self.item(row_index, json_dict_key_index).text()
                    # 获取对象在数据列表中的索引
                    key_index = self.datas_key_col_value_list.index(key_value)
                    # 判断不是df对象，即为Model对象
                    if self.data_type != "df":
                        # 获取所改变的单元格对应的对象
                        model = self.datas[key_index]
                        # 获取对象中的json字典
                        json_dict = getattr(model, self.json_attr_name)
                        # 改变json字典中单元格对应key的值
                        json_dict[json_dict_key_value][col_name] = value
                        # 将修改后的字典重新赋值给对象
                        setattr(model, self.json_attr_name, json_dict)
                        # 将json字典转为字符串，用于后续的存入数据库操作
                        json_dict_str = json.dumps(json_dict)
                        if key_value in self.change_dic.keys():
                            self.change_dic[key_value][self.json_attr_name] = json_dict_str
                        else:
                            self.change_dic[key_value] = {self.json_attr_name: json_dict_str}
                        return

            if key_value in self.change_dic.keys():
                self.change_dic[key_value][col_name] = value
            else:
                self.change_dic[key_value] = {col_name: value}
            # print(f"已修改第{row_index}行的{col_name}值为:{value}")
            # print("已修改的值为：",self.change_dic)

    # endregion
    # 获取单元格
    def get_cell_widget(self, row, column):
        """获取单元格的控件"""
        return self.cellWidget(row, column)

    # region 获取所选的数据信息可以是DataFrame也可以是Model
    def get_selected_rows(self):
        datas = self.datas
        if self.key_col_name:
            select_key_list = self.get_selected_key_values()
            if self.data_type == "df":
                selected_df = datas.loc[datas[self.key_col_name].isin(select_key_list)]
                return selected_df
            else:
                return [datas[i] for i in select_key_list]

    def get_selected_key_values(self):
        """如何有关键列名，则返回关键行的数据，若无则返回行序号"""
        selected_cells = self.selectedItems()
        # 提取行索引
        selected_rows = set()
        for cell in selected_cells:
            selected_rows.add(cell.row())
        if self.key_col_name:
            col_index = self.col_name_list.index(self.key_col_name)
            select_data_list = []
            for row_index in selected_rows:
                data = self.item(row_index, col_index).text()
                if self.key_col_name == "id":
                    data = int(data)
                select_data_list.append(data)
            return select_data_list
        return selected_rows

    def save_change_to_db(self):
        """
        将修改保存至数据表中
        """
        change_dict_to_db(self.tb_name, self.change_dic, self.col_name_dic[self.key_col_name].value_attr_name)

    # endregion


